/**
 * 请勿修改此处代码，因为插件更新后此处代码会被覆盖。
 * 作者：VK
 * 发布于：2021-07-06
 */

const configCenter = require("uni-config-center");
const config = configCenter({ pluginId: 'uni-pay' }).requireFile('config.js');
const crypto = require("crypto");

var util = {};
util.aes = {};
const aesKey = "5fb2cd73c7b53918728417c50762e6d45fb2cd73c7b53918728417c50762e6d4";

// 当前支持的加密算法选项
const AESMODE = {
	AES192: "aes192", // 支持64位或64以上密钥，但仅node.js支持
	AES256ECB: "aes-256-ecb", // 兼容java、php等其他后端语言，密钥长度固定32位（若超过32位，会自动截取前32位）
};
util.aes.MODE = AESMODE;
/**
 * aes加密
 * @param {String} mode 默认为aes192支持64位或64以上密钥 其他可选（aes-256-ecb 兼容java、php等其他后端语言，但密钥长度需固定16位）
 * @param {Object} data 待加密的原文
 * @param {Object} key 密钥，如不传，自动取config
 * 调用示例
let encrypted = vkPay.crypto.aes.encrypt({
	mode: "aes192",
	data: "", // 待加密的原文
});
 */
util.aes.encrypt = function(obj) {
	let {
		data, // 待加密的原文
		key, // 密钥，如不传，自动取config
		mode = AESMODE.AES192,
	} = obj;
	if (!key) key = config.notifyKey || aesKey;
	if (typeof data === "object") data = JSON.stringify(data);
	if (mode === AESMODE.AES256ECB) {
		// aes-256-ecb算法
		return encryptUseAes256Ecb(data, key);
	} else if (mode === AESMODE.AES192) {
		// aes192算法
		return encryptUseAes192(data, key);
	} else {
		throw new Error(`msg:不支持 ${mode} 加密算法`);
	}
};

/**
 * aes解密
 * @param {String} mode 默认为aes192支持64位或64以上密钥 其他可选（aes-256-ecb 兼容java、php等其他后端语言，但密钥长度需固定16位）
 * @param {Object} data 待解密的原文
 * @param {Object} key 密钥，如不传，自动取config
 * 调用示例
let decrypted = vkPay.crypto.aes.decrypt({
	mode: "aes192",
	data: "", // 待解密的原文
});
 */
util.aes.decrypt = function(obj) {
	let {
		data, // 待解密的原文
		key, // 密钥，如不传，自动取config
		mode = AESMODE.AES192,
	} = obj;
	if (typeof data === "undefined") {
		throw new Error("msg:待解密原文不能为空");
	}
	if (!key) key = config.notifyKey || aesKey;
	// 解密
	if (mode === AESMODE.AES256ECB) {
		// aes-256-ecb算法
		return decryptUseAes256Ecb(data, key);
	} else if (mode === AESMODE.AES192) {
		// aes192算法
		return decryptUseAes192(data, key);
	} else {
		throw new Error(`msg:不支持 ${mode} 加密算法`);
	}
};

module.exports = util;

// aes192算法 - 加密
function encryptUseAes192(data, key) {
	const cipher = crypto.createCipher(AESMODE.AES192, key);
	let encrypted = cipher.update(data, 'utf8', 'hex');
	encrypted += cipher.final('hex');
	// encrypted 为加密后的内容
	return encrypted;
}

// aes192算法 - 解密
function decryptUseAes192(data, key) {
	// aes192 算法
	let decrypted;
	try {
		const decipher = crypto.createDecipher(AESMODE.AES192, key);
		decrypted = decipher.update(data, 'hex', 'utf8');
		decrypted += decipher.final('utf8');
		try {
			decrypted = JSON.parse(decrypted);
		} catch (err) {}
	} catch (err) {
		throw "msg:解密失败";
	}
	// decrypted 为解密后的内容，即最开始需要加密的原始数据文本data
	return decrypted;
}

// aes-256-ecb算法 - 加密
function encryptUseAes256Ecb(data, key) {
	let paddedData = Buffer.from(data);
	let paddedkey = key;
	if (paddedkey.length > 32) {
		paddedkey = paddedkey.substring(0, 32);
	}
	paddedkey = Buffer.from(paddedkey);
	const cipher = crypto.createCipheriv(AESMODE.AES256ECB, paddedkey, '');
	cipher.setAutoPadding(false);
	const blockSize = 16; // AES块大小为16字节
	const paddingSize = blockSize - (paddedData.length % blockSize);
	const paddingBuffer = Buffer.alloc(paddingSize, paddingSize);
	paddedData = Buffer.concat([paddedData, paddingBuffer]);
	let encrypted = cipher.update(paddedData, null, 'base64');
	encrypted += cipher.final('base64');
	return encrypted;
}

// aes-256-ecb算法 - 解密
function decryptUseAes256Ecb(data, key) {
	let paddedkey = key;
	if (paddedkey.length > 32) {
		paddedkey = paddedkey.substring(0, 32);
	}
	paddedkey = Buffer.from(paddedkey);
	let decrypted;
	try {
		const decipher = crypto.createDecipheriv(AESMODE.AES256ECB, paddedkey, '');
		decipher.setAutoPadding(false);

		let decrypted = decipher.update(data, 'base64');
		decrypted += decipher.final();

		const lastByte = decrypted.charCodeAt(decrypted.length - 1);
		const paddingSize = lastByte;
		decrypted = decrypted.slice(0, decrypted.length - paddingSize);
		try {
			decrypted = JSON.parse(decrypted);
		} catch (err) {}
		return decrypted;
	} catch (err) {
		throw "解密失败";
	}
	return decrypted;
}
